using System;
using System.Collections.Generic;
using System.Text;
using org.apache.mina.common;
using java.lang;
using java.net;
using java.io;

namespace PDSClient.Utils.IO.Impl
{
    public class SocketConnector : IConnector<SocketAddress>
    {
        private IoConnector connector;

        private SocketEndpoint endpoint;

        private ConnectorConnListener connListener = null;

        private ConnectFuture connectFuture;

        public SocketConnector(SocketEndpoint endpoint, IoConnector connector)
        {    
            this.endpoint = endpoint;
            this.connector = connector;
        }

        public void Connect(IConnectionListener listener)
        {
            lock(this)
            {
                if (this.connListener != null) {
                    System.Exception e = new System.Exception("Connection already in progress");
                    //logging logger.logThrow(Level.FINE, e, e.getMessage());
                    throw e;
                }
                connListener = new ConnectorConnListener(listener);
            }
            //logging logger.log(Level.FINE, "connecting to {0}", endpoint);
            ConnectFuture future = connector.connect(endpoint.Address, connListener);
            lock (this)
            {
                connectFuture = future;
            }
        }

        public bool WaitForConnect(long timeout)
        {
            ConnectFuture future;
            lock (this)
            {
                future = connectFuture;
            }
            if(future == null)
                throw new System.Exception("IllegalStateException, No connect attempt in progress");

            if (!future.isConnected())
                future.join(timeout);

            bool ready = future.isReady();
            if (ready)
            {
                try
                {
                    future.getSession();
                }
                catch (RuntimeIOException e)
                { 
		            System.Exception t = e.getCause();
		            if (t is IOException) {
		                throw (IOException) t;
		            }
                }
            }
            return ready;
        }

        public bool Connected
        {
            get { 
                lock (this) 
                {
                    if (connectFuture == null)
                        return false;
                }
                return connectFuture.isConnected();
            }
        }

        public IEndpoint<SocketAddress> Endpoint
        {
            get { return endpoint; }
        }

        public void ShutDown()
        {
            //logging logger.log(Level.FINE, "shutdown called");
            lock (this)
            {
                if (connListener == null)
                {
                    System.Exception e = new System.Exception(
                        "No connection in progress");
                    //logging logger.logThrow(Level.FINE, e, e.getMessage());
                    throw e;
                }
            }
            connListener.Cancel();
        }

        public sealed class ConnectorConnListener : SocketConnectionListener 
        {
            private IConnectionListener listener;

            private bool cancelled = false;

            private bool connected = false;

            public ConnectorConnListener(IConnectionListener listener)
            {
                this.listener = listener;
            }

            public void Cancel()
            {
                lock (this)
                {
                    if (connected)
                    {
                        System.Exception e = new System.Exception("Already connected");
                        //logging logger.logThrow(Level.FINE, e, e.getMessage());
                        throw e;
                    }
                    if (cancelled)
                    {
                        System.Exception e = new System.Exception("already cancelled");
                        //logging logger.logThrow(Level.FINE, e, e.getMessage());
                        throw e;
                    }
                    cancelled = true;
                }
            }

            public override void sessionCreated(IoSession session)
            { 
                lock(this)
                {
                    if(cancelled)
                    {
                        //logging logger.log(Level.FINE, "cancelled; ignore created session {0}",session);
                        session.close();
                        return;
                    }
                    connected = true;
                }
                // logger.log(Level.FINE, "created session {0}", session);
                CompleteMessageFilter filter = new CompleteMessageFilter();
                SocketConnection connection = new SocketConnection(listener, filter, session);
                session.setAttachment(connection);
            }
        }
    }
}
